Core elements of a ggplot plot:
(compiled from: https://ourcodingclub.github.io/tutorials/datavis/)
geom
Geometric object which defines the type of graph you are making.
It reads your data in the aesthetics mapping to know which variables
to use, and creates the graph accordingly.
Some common types are:
geom_point()
geom_boxplot()
geom_histogram()
geom_col()
aes
Short for aesthetics.
Usually placed within a geom_, this is where you specify your data
source and variables, AND the properties of the graph which depend on
those variables.
For instance, if you want all data points to be the same colour, you
would define the ‘colour =’ argument outside the aes()
function; if you want the data points to be coloured by a factor’s
levels (e.g. by site or species), you specify the colour = argument
inside the aes().
Some common things to include in aes are:
x
y
fill
colour
size
shape
But note that different geoms have different
aesthetics available (see cheatsheet below for example)
stat
a stat layer applies some statistical transformation to the
underlying data: for instance, stat_smooth(method = ‘lm’) displays a
linear regression line and confidence interval ribbon on top of a
scatter plot (defined with geom_point()).
theme
A set of visual parameters that control the background, borders, grid
lines, axes, text size, legend position, etc.
You can use pre-defined themes (e.g., theme_complot() from the
cowplot package), create your own, or use a predefined theme and
overwrite only the elements you don’t like.
Examples of elements within themes are:
e.g., axis.text.y = element_text(size = 12)
e.g., axis.text.x = element_text(size = 12, angle = 45, vjust = 1,
hjust = 1)
[makes the x labels at an angle]
e.g., axis.title = element_text(size = 14, face = “plain”)
e.g., panel.grid = element_blank()
[Removes the background grid lines]
e.g., plot.margin = unit(c(1,1,1,1), units = , “cm”)
[Adds a 1cm margin around the plot]
e.g., legend.text = element_text(size = 12, face = “italic”)
[Setting the font for the legend text]
e.g., legend.title = element_blank()
[Remove the legend title - useful as sometimes this is excessive and
the default is to include it]
e.g., legend.position = c(0.9, 0.9)))
+theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1,
hjust = 1),
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = “plain”),
panel.grid = element_blank(),
plot.margin = unit(c(1,1,1,1), units = , “cm”),
legend.text = element_text(size = 12, face = “italic”),
legend.title = element_blank(),
legend.position = c(0.9, 0.9))
You define their properties with elements_…() functions. For
example:
element_blank() would return something empty (ideal for removing
background colour),
element_text(size = …, face = …, angle = …) lets you control all
kinds of text properties.
# theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1,
hjust = 1), # making the years at a bit of an angle
#try a plot of home runs over year
ggplot(dat, aes(x=yearID, y=H))+geom_point()

#equivalent to
ggplot(dat)+geom_point(aes(x=yearID, y=H))

top tip: by encircling the ggplot in parenthesis () you get to assign
a plot to a variable and plot it at the same time. useful if you want to
save the plot or make it into a figure, refer to it later (e.g., replot,
put in a panel with other figs) etc. Example here using the same plot as
above
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)))

remove grey background with +theme_bw()
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_bw())
many other themes are available
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_classic())

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_minimal())

(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_cowplot())

you can also create your own theme!
Just write it as a function. Example here taken from: https://rpubs.com/jenrichmond/W6LL
#library(data.table)
#library(palmerpenguins)
#library(cowplot)
##library(ggplot)
theme_jen <- function () {
# define font up front
font <- "Helvetica"
# this theme uses theme_bw as the base
theme_bw() %+replace%
theme(
#get rid of grid lines/borders
panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
# add white space top, right, bottom, left
plot.margin = unit(c(1, 1, 1, 1), "cm"),
# custom axis title/text/lines
axis.title = element_text(
family = font,
size = 14),
axis.text = element_text(
family = font,
size = 12),
# margin pulls text away from axis
axis.text.x = element_text(
margin=margin(5, b = 10)),
# black lines
axis.line = element_line(colour = "black", size = rel(1)),
# custom plot titles, subtitles, captions
plot.title = element_text(
family = font,
size = 18,
hjust = -0.1,
vjust = 4),
# custom plot subtitles
plot.subtitle = element_text(
family = font,
size = 14,
hjust = 0,
vjust = 3),
# custom captions
plot.caption = element_text(
family = font,
size = 10,
hjust = 1,
vjust = 2),
# custom legend
legend.title = element_text(
family = font,
size = 10,
hjust = 0),
legend.text = element_text(
family = font,
size = 8,
hjust = 0),
#no background on legend
legend.key = element_blank(),
# white background on plot
strip.background = element_rect(fill = "white",
colour = "black",
size = rel(2)), complete = TRUE)
}
#source("theme_jen.R") # the script/function containing custom ggplot theme
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) + theme_jen())

add label to x and y axis plus add in various elements of theme
(plot1 = ggplot(dat)+geom_point(aes(x=yearID, y=H)) +
theme_classic()+
xlab('\nyear')+#\n adds blank line
ylab('n home runs')+ #\nadds blank line
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), # making the years at a bit of an angle
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = "plain"),
panel.grid = element_blank(),# Removing the background grid lines
plot.margin = unit(c(1,1,1,1), units = , "cm"), # Adding a 1cm margin around the plot
legend.text = element_text(size = 12, face = "italic"), # Setting the font for the legend text
legend.title = element_blank(), # Removing the legend title
legend.position = c(0.9, 0.9)))

might be claner to do the same plot on mean H per year
(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID])+geom_point(aes(x=yearID, y=H)) + theme_classic()+
xlab('\nyear')+
ylab('mean home runs per year')+
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = "plain"),
panel.grid = element_blank(),
plot.margin = unit(c(1,1,1,1), units = , "cm"),
legend.text = element_text(size = 12, face = "italic"),
legend.title = element_blank(),
legend.position = c(0.9, 0.9)))

add a linear trendline using geom_smooth have to specficy method for
this (method=“lm” or method=lm is fine). se is added by default (can add
se=F to disable this)
(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
geom_point()+
geom_smooth(method=lm)+
theme_classic()+
xlab('\nyear')+
ylab('mean home runs per year')+
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1), axis.text.y = element_text(size = 12), axis.title = element_text(size = 14, face = "plain"), panel.grid = element_blank(),plot.margin = unit(c(1,1,1,1), units = , "cm"), legend.text = element_text(size = 12, face = "italic"), legend.title = element_blank(),legend.position = c(0.9, 0.9)))
`geom_smooth()` using formula 'y ~ x'

you can also add a specific formula in geom_smooth (e.g.,
y~x+x2+x3)
(plot1 = ggplot(dat[, .(H=mean(H)), by=yearID], aes(x=yearID, y=H))+
geom_point()+
geom_smooth(formula=y~x+x^2+x^3)+
theme_classic()+
xlab('\nyear')+
ylab('mean home runs per year')+
theme(axis.text.x = element_text(size = 12, angle = 45, vjust = 1, hjust = 1),
axis.text.y = element_text(size = 12),
axis.title = element_text(size = 14, face = "plain"),
panel.grid = element_blank(),
plot.margin = unit(c(1,1,1,1), units = , "cm"),
legend.text = element_text(size = 12, face = "italic"),
legend.title = element_blank(),
legend.position = c(0.9, 0.9)))
`geom_smooth()` using method = 'loess'

facet wrap this can be used to easily plot data in panels (e.g., plot
mean home runs over time for each leagueID - here I also distinguish
leagues by colour)/ seeing scales = “free_y” below means the y axis can
vary from plot to plot. You can also use nrow = or
ncol = to specify the numbers of rows/columns
dat$yearIDfact = as.factor(dat$yearID)
(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
geom_point()+
facet_wrap(vars(lgID), scales = "free_y")+
theme_classic()+
xlab('\nyear')+ #\n adds blank line
ylab('mean home runs per year'))

facet_grid does a similar thing but organised into columns of
rows
here use rows based on teamID
(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
geom_point()+
facet_grid(lgID ~ .)+
theme_classic()+
xlab('\nyear')+ #\n adds blank line
ylab('mean home runs per year'))

columns based on teamID
(plot1 = ggplot(dat[, .(H=mean(H)), by=.(lgID, yearID)], aes(x=yearID, y=H, colour=lgID))+
geom_point()+
facet_grid(. ~ lgID)+
theme_classic()+
xlab('\nyear')+ #\n adds blank line
ylab('mean home runs per year'))

LS0tCnRpdGxlOiAiZ2dwbG90X3RpY2tzdHJpY2tzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKIyMjIGNsZWFyIGVudmlyb25tZW50CgpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCmBgYAoKIyMjIGxvYWQgcGFja2FnZXMgKGluc3RhbGwgaW4geW91IGRvbid0IGhhdmUpCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGNvbG91cnBpY2tlcikgI2FkZGluIHVzZWZ1bCBmb3Igc2VsZWN0aW5nIGNvbG91cnMKbGlicmFyeShjb3dwbG90KSAjdGhlbWUgZm9yIHBsb3R0aW5nCmxpYnJhcnkoZGF0YS50YWJsZSkgI3BhY2thZ2UgZm9yIG1hbmlwdWxhdGluZyBhbmQgY29tcHV0aW5nIG9uIGRhdGEKbGlicmFyeShyYWluY2xvdWRwbG90cykgI2h0dHBzOi8vd2VsbGNvbWVvcGVucmVzZWFyY2gub3JnL2FydGljbGVzLzQtNjMKYGBgCgojIyMgcGFja2FnZXMgdGhhdCBoYXZlIGRhdGEgc2V0cyBpbiB0aGVtCgpgYGB7cn0KbGlicmFyeShMYWhtYW4pICNoYXMgZGF0YSBzZXRzIHJlbGF0ZWQgdG8gYmFzZWJhbGwgKEFsbHN0YXIgYW5kIFBpdGNoaW5nKQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKSAjaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zIChwZW5ndWlucykKCmBgYAoKIyMjIGxvYWQgaW4gZGF0YQoKQWxsc3RhckZ1bGwgZnJvbSBMYWhtYW4gcGFja2FnZSBoYXMgYmFzZWJhbGwgc3RhdHMuCgpQaXRjaGluZyBpcyBhbm90aGVyIGRhdGEgc2V0IGluY2x1ZGVkIHdpdGggTGFobWFuIHBhY2thZ2UgYW5kIGlzIG1vcmUKY29tcHJlaGVuc2l2ZS4KCnBhbG1lcnBlbmd1aW5zIHBhY2thZ2UgaGFzIGRhdGEgc2V0cyByZWxhdGVkIHRvIHBlbmd1aW5zLgoKZ2dwbG90IGFsc28gaGFzIGRhdGEgc2V0IG1pZHdlc3QgaW5jbHVkZWQgd2l0aCBpdC4gTG9hZCB0aGlzIGJ5IGRvaW5nCipkYXRhKCJtaWR3ZXN0IiwgcGFja2FnZSA9ICJnZ3Bsb3QyIikqCgpOZWVkIHRvIGNvcHkgYW5kIHRoZW4gY29udmVydCB0aGUgZGF0YSB0byBhIGRhdGEgdGFibGUKCmBgYHtyfQpkYXQgPSBjb3B5KFBpdGNoaW5nKQpjbGFzcyhkYXQpCnNldERUKGRhdCkKY2xhc3MoZGF0KQpgYGAKCmV4YW1pbmUgZGF0YQoKYGBge3J9CmhlYWQoZGF0KQpzdHIoZGF0KQpgYGAKCiMjIyAqKm5vdGVzIG9uIGRhdGEqKgoKKipwbGF5ZXJJRDoqKiBQbGF5ZXIgSUQgY29kZQoKKip5ZWFySUQ6KiogWWVhcgoKKipzdGludDoqKiBwbGF5ZXIncyBzdGludCAob3JkZXIgb2YgYXBwZWFyYW5jZXMgd2l0aGluIGEgc2Vhc29uKQoKKip0ZWFtSUQ6KiogVGVhbSAoZmFjdG9yKQoKKipsZ0lEOioqIExlYWd1ZSBJRCBhIGZhY3RvciB3aXRoIGxldmVscyBBQSwgQUwsIEZMLCBOTCwgUEwsIFVBCgoqKlc6KiogV2lucwoKKipMOioqIExvc3NlcwoKKipHOioqIEdhbWVzCgoqKkdTOioqIEdhbWVzIFN0YXJ0ZWQKCioqQ0c6KiogQ29tcGxldGUgR2FtZXMKCioqU0hPOioqIFNodXRvdXRzCgoqKlNWOioqIFNhdmVzIElQb3V0cyBPdXRzIFBpdGNoZWQgKGlubmluZ3MgcGl0Y2hlZCB4IDMpCgoqKkg6KiogSGl0cwoKKipFUjoqKiBFYXJuZWQgUnVucwoKKipIUjoqKiBIb21lcnVucwoKKipCQjoqKiBXYWxrcwoKKipTTzoqKiBTdHJpa2VvdXRzCgoqKkJBT3BwOioqIE9wcG9uZW50J3MgQmF0dGluZyBBdmVyYWdlCgoqKkVSQToqKiBFYXJuZWQgUnVuIEF2ZXJhZ2UKCioqSUJCOioqIEludGVudGlvbmFsIFdhbGtzCgoqKldQOioqIFdpbGQgUGl0Y2hlcwoKKipIQlA6KiogQmF0dGVycyBIaXQgQnkgUGl0Y2gKCioqQks6KiogQmFsa3MKCioqQkZQOioqIEJhdHRlcnMgZmFjZWQgYnkgUGl0Y2hlcgoKKipHRjoqKiBHYW1lcyBGaW5pc2hlZCBSIFJ1bnMgQWxsb3dlZAoKKipTSDoqKiBTYWNyaWZpY2VzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqU0Y6KiogU2FjcmlmaWNlIGZsaWVzIGJ5IG9wcG9zaW5nIGJhdHRlcnMKCioqR0lEUDoqKiBHcm91bmRlZCBpbnRvIGRvdWJsZSBwbGF5cyBieSBvcHBvc2luZyBiYXR0ZXIKCiMjIyAqKkNvcmUgZWxlbWVudHMgb2YgYSBnZ3Bsb3QgcGxvdDoqKgoKKGNvbXBpbGVkIGZyb206IDxodHRwczovL291cmNvZGluZ2NsdWIuZ2l0aHViLmlvL3R1dG9yaWFscy9kYXRhdmlzLz4pCgoqKmdlb20qKgoKR2VvbWV0cmljIG9iamVjdCB3aGljaCBkZWZpbmVzIHRoZSB0eXBlIG9mIGdyYXBoIHlvdSBhcmUgbWFraW5nLgoKSXQgcmVhZHMgeW91ciBkYXRhIGluIHRoZSBhZXN0aGV0aWNzIG1hcHBpbmcgdG8ga25vdyB3aGljaCB2YXJpYWJsZXMgdG8KdXNlLCBhbmQgY3JlYXRlcyB0aGUgZ3JhcGggYWNjb3JkaW5nbHkuCgpTb21lIGNvbW1vbiB0eXBlcyBhcmU6CgotICAgZ2VvbV9wb2ludCgpCgotICAgZ2VvbV9ib3hwbG90KCkKCi0gICBnZW9tX2hpc3RvZ3JhbSgpCgotICAgZ2VvbV9jb2woKQoKKiphZXMqKgoKU2hvcnQgZm9yIGFlc3RoZXRpY3MuCgpVc3VhbGx5IHBsYWNlZCB3aXRoaW4gYSBnZW9tXF8sIHRoaXMgaXMgd2hlcmUgeW91IHNwZWNpZnkgeW91ciBkYXRhCnNvdXJjZSBhbmQgdmFyaWFibGVzLCBBTkQgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdyYXBoIHdoaWNoIGRlcGVuZCBvbgp0aG9zZSB2YXJpYWJsZXMuCgpGb3IgaW5zdGFuY2UsIGlmIHlvdSB3YW50IGFsbCBkYXRhIHBvaW50cyB0byBiZSB0aGUgc2FtZSBjb2xvdXIsIHlvdQp3b3VsZCBkZWZpbmUgdGhlICdjb2xvdXIgPScgYXJndW1lbnQgKm91dHNpZGUqIHRoZSBhZXMoKSBmdW5jdGlvbjsgaWYKeW91IHdhbnQgdGhlIGRhdGEgcG9pbnRzIHRvIGJlIGNvbG91cmVkIGJ5IGEgZmFjdG9yJ3MgbGV2ZWxzIChlLmcuIGJ5CnNpdGUgb3Igc3BlY2llcyksIHlvdSBzcGVjaWZ5IHRoZSBjb2xvdXIgPSBhcmd1bWVudCAqaW5zaWRlKiB0aGUgYWVzKCkuCgpTb21lIGNvbW1vbiB0aGluZ3MgdG8gaW5jbHVkZSBpbiBhZXMgYXJlOgoKLSAgIHgKCi0gICB5CgotICAgZmlsbAoKLSAgIGNvbG91cgoKLSAgIHNpemUKCi0gICBzaGFwZQoKKipCdXQqKiBub3RlIHRoYXQgZGlmZmVyZW50IGdlb21zIGhhdmUgZGlmZmVyZW50IGFlc3RoZXRpY3MgYXZhaWxhYmxlCihzZWUgY2hlYXRzaGVldCBiZWxvdyBmb3IgZXhhbXBsZSkKCioqc3RhdCoqCgphIHN0YXQgbGF5ZXIgYXBwbGllcyBzb21lIHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uIHRvIHRoZSB1bmRlcmx5aW5nCmRhdGE6IGZvciBpbnN0YW5jZSwgc3RhdF9zbW9vdGgobWV0aG9kID0gJ2xtJykgZGlzcGxheXMgYSBsaW5lYXIKcmVncmVzc2lvbiBsaW5lIGFuZCBjb25maWRlbmNlIGludGVydmFsIHJpYmJvbiBvbiB0b3Agb2YgYSBzY2F0dGVyIHBsb3QKKGRlZmluZWQgd2l0aCBnZW9tX3BvaW50KCkpLgoKKip0aGVtZSoqCgpBIHNldCBvZiB2aXN1YWwgcGFyYW1ldGVycyB0aGF0IGNvbnRyb2wgdGhlIGJhY2tncm91bmQsIGJvcmRlcnMsIGdyaWQKbGluZXMsIGF4ZXMsIHRleHQgc2l6ZSwgbGVnZW5kIHBvc2l0aW9uLCBldGMuCgpZb3UgY2FuIHVzZSBwcmUtZGVmaW5lZCB0aGVtZXMgKGUuZy4sIHRoZW1lX2NvbXBsb3QoKSBmcm9tIHRoZSBjb3dwbG90CnBhY2thZ2UpLCBjcmVhdGUgeW91ciBvd24sIG9yIHVzZSBhIHByZWRlZmluZWQgdGhlbWUgYW5kIG92ZXJ3cml0ZSBvbmx5CnRoZSBlbGVtZW50cyB5b3UgZG9uJ3QgbGlrZS4KCkV4YW1wbGVzIG9mIGVsZW1lbnRzIHdpdGhpbiB0aGVtZXMgYXJlOgoKLSAgICoqYXhpcy50ZXh0KioKCmUuZy4sIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikKCmUuZy4sIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdAo9IDEpCgpbbWFrZXMgdGhlIHggbGFiZWxzIGF0IGFuIGFuZ2xlXQoKLSAgICoqYXhpcy50aXRsZSoqCgplLmcuLCBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJwbGFpbiIpCgotICAgKipwYW5lbC5ncmlkKioKCmUuZy4sIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkKCltSZW1vdmVzIHRoZSBiYWNrZ3JvdW5kIGdyaWQgbGluZXNdCgotICAgKipwbG90Lm1hcmdpbioqCgplLmcuLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpCgpbQWRkcyBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90XQoKLSAgICoqbGVnZW5kIHRleHQqKgoKZS5nLiwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpCgpbU2V0dGluZyB0aGUgZm9udCBmb3IgdGhlIGxlZ2VuZCB0ZXh0XQoKLSAgICoqbGVnZW5kLnRpdGxlKioKCmUuZy4sIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKQoKW1JlbW92ZSB0aGUgbGVnZW5kIHRpdGxlIC0gdXNlZnVsIGFzIHNvbWV0aW1lcyB0aGlzIGlzIGV4Y2Vzc2l2ZSBhbmQgdGhlCmRlZmF1bHQgaXMgdG8gaW5jbHVkZSBpdF0KCi0gICAqKmxlZ2VuZCBwb3NpdGlvbioqCgplLmcuLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCgotICAgKipwdXR0aW5nIGl0IGFsbCB0b2dldGhlci4uLioqCgordGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBhbmdsZSA9IDQ1LCB2anVzdCA9IDEsCmhqdXN0ID0gMSksCgpheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAoKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwKCnBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCgpwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLAoKbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gIml0YWxpYyIpLAoKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAoKbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpCgpZb3UgZGVmaW5lIHRoZWlyIHByb3BlcnRpZXMgd2l0aCBlbGVtZW50c1xfLi4uKCkgZnVuY3Rpb25zLiBGb3IgZXhhbXBsZToKCmVsZW1lbnRfYmxhbmsoKSB3b3VsZCByZXR1cm4gc29tZXRoaW5nIGVtcHR5IChpZGVhbCBmb3IgcmVtb3ZpbmcKYmFja2dyb3VuZCBjb2xvdXIpLAoKZWxlbWVudF90ZXh0KHNpemUgPSAuLi4sIGZhY2UgPSAuLi4sIGFuZ2xlID0gLi4uKSBsZXRzIHlvdSBjb250cm9sIGFsbApraW5kcyBvZiB0ZXh0IHByb3BlcnRpZXMuCgpcIyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwKaGp1c3QgPSAxKSwgXCMgbWFraW5nIHRoZSB5ZWFycyBhdCBhIGJpdCBvZiBhbiBhbmdsZQoKYGBge3J9CiN0cnkgYSBwbG90IG9mIGhvbWUgcnVucyBvdmVyIHllYXIKZ2dwbG90KGRhdCwgYWVzKHg9eWVhcklELCB5PUgpKStnZW9tX3BvaW50KCkKI2VxdWl2YWxlbnQgdG8KZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpCmBgYAoKdG9wIHRpcDogYnkgZW5jaXJjbGluZyB0aGUgZ2dwbG90IGluIHBhcmVudGhlc2lzICgpIHlvdSBnZXQgdG8gYXNzaWduIGEKcGxvdCB0byBhIHZhcmlhYmxlIGFuZCBwbG90IGl0IGF0IHRoZSBzYW1lIHRpbWUuIHVzZWZ1bCBpZiB5b3Ugd2FudCB0bwpzYXZlIHRoZSBwbG90IG9yIG1ha2UgaXQgaW50byBhIGZpZ3VyZSwgcmVmZXIgdG8gaXQgbGF0ZXIgKGUuZy4sIHJlcGxvdCwKcHV0IGluIGEgcGFuZWwgd2l0aCBvdGhlciBmaWdzKSBldGMuIEV4YW1wbGUgaGVyZSB1c2luZyB0aGUgc2FtZSBwbG90IGFzCmFib3ZlCgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdCkrZ2VvbV9wb2ludChhZXMoeD15ZWFySUQsIHk9SCkpKQpgYGAKCnJlbW92ZSBncmV5IGJhY2tncm91bmQgd2l0aCArdGhlbWVfYncoKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2J3KCkpCmBgYAoKbWFueSBvdGhlciB0aGVtZXMgYXJlIGF2YWlsYWJsZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX21pbmltYWwoKSkKCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2Nvd3Bsb3QoKSkKYGBgCgp5b3UgY2FuIGFsc28gY3JlYXRlIHlvdXIgb3duIHRoZW1lIQoKSnVzdCB3cml0ZSBpdCBhcyBhIGZ1bmN0aW9uLiBFeGFtcGxlIGhlcmUgdGFrZW4gZnJvbToKPGh0dHBzOi8vcnB1YnMuY29tL2plbnJpY2htb25kL1c2TEw+CgpgYGB7cn0KI2xpYnJhcnkoZGF0YS50YWJsZSkKI2xpYnJhcnkocGFsbWVycGVuZ3VpbnMpCiNsaWJyYXJ5KGNvd3Bsb3QpCiMjbGlicmFyeShnZ3Bsb3QpCgp0aGVtZV9qZW4gPC0gZnVuY3Rpb24gKCkgewogIAogICMgZGVmaW5lIGZvbnQgdXAgZnJvbnQKICBmb250IDwtICJIZWx2ZXRpY2EiICAKICAjIHRoaXMgdGhlbWUgdXNlcyB0aGVtZV9idyBhcyB0aGUgYmFzZSAKICAKICB0aGVtZV9idygpICUrcmVwbGFjZSUgICAKICAgIHRoZW1lKAogICAgICAjZ2V0IHJpZCBvZiBncmlkIGxpbmVzL2JvcmRlcnMKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAjIGFkZCB3aGl0ZSBzcGFjZSB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQKICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMSwgMSwgMSksICJjbSIpLCAKICAgICAgIyBjdXN0b20gYXhpcyB0aXRsZS90ZXh0L2xpbmVzCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTQpLCAgICAgICAgICAgICAgIAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgc2l6ZSA9IDEyKSwgICAKICAgICAgIyBtYXJnaW4gcHVsbHMgdGV4dCBhd2F5IGZyb20gYXhpcwogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgIAogICAgICAgIG1hcmdpbj1tYXJnaW4oNSwgYiA9IDEwKSksCiAgICAgICMgYmxhY2sgbGluZXMKICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIsIHNpemUgPSByZWwoMSkpLCAKICAgICAgIyBjdXN0b20gcGxvdCB0aXRsZXMsIHN1YnRpdGxlcywgY2FwdGlvbnMKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxOCwKICAgICAgICBoanVzdCA9IC0wLjEsCiAgICAgICAgdmp1c3QgPSA0KSwKICAgICAgICMgY3VzdG9tIHBsb3Qgc3VidGl0bGVzCiAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgIAogICAgICAgIGZhbWlseSA9IGZvbnQsICAgICAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxNCwgCiAgICAgICAgaGp1c3QgPSAwLAogICAgICAgIHZqdXN0ID0gMyksCiAgICAgICAjIGN1c3RvbSBjYXB0aW9ucwogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoICAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgICAgICAKICAgICAgICBzaXplID0gMTAsCiAgICAgICAgaGp1c3QgPSAxLAogICAgICAgIHZqdXN0ID0gMiksIAogICAgICAjIGN1c3RvbSBsZWdlbmQgCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgCiAgICAgICAgZmFtaWx5ID0gZm9udCwgICAgICAgICAgIAogICAgICAgIHNpemUgPSAxMCwgICAgICAgICAgICAgICAgCiAgICAgICAgaGp1c3QgPSAwKSwgCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KCAgICAgICAgICAKICAgICAgICBmYW1pbHkgPSBmb250LCAgICAgICAgICAgICAgIAogICAgICAgIHNpemUgPSA4LCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgIGhqdXN0ID0gMCksIAogICAgICAjbm8gYmFja2dyb3VuZCBvbiBsZWdlbmQKICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwgICAKICAgICAgIyB3aGl0ZSBiYWNrZ3JvdW5kIG9uIHBsb3QKICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSByZWwoMikpLCBjb21wbGV0ZSA9IFRSVUUpCiAgCn0KYGBgCgpgYGB7cn0KI3NvdXJjZSgidGhlbWVfamVuLlIiKSAjIHRoZSBzY3JpcHQvZnVuY3Rpb24gY29udGFpbmluZyBjdXN0b20gZ2dwbG90IHRoZW1lCihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2plbigpKQpgYGAKCmFkZCBsYWJlbCB0byB4IGFuZCB5IGF4aXMgcGx1cyBhZGQgaW4gdmFyaW91cyBlbGVtZW50cyBvZiB0aGVtZQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXQpK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIAp0aGVtZV9jbGFzc2ljKCkrCnhsYWIoJ1xueWVhcicpKyNcbiBhZGRzIGJsYW5rIGxpbmUKeWxhYignbiBob21lIHJ1bnMnKSsgI1xuYWRkcyBibGFuayBsaW5lCnRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpLCAjIG1ha2luZyB0aGUgeWVhcnMgYXQgYSBiaXQgb2YgYW4gYW5nbGUKYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgICAgICAgICAgICAgICAgICAgICAgICAKcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwjIFJlbW92ZSB0aGUgYmFja2dyb3VuZCBncmlkIGxpbmVzICAgICAgIApwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAjIEFkZCBhIDFjbSBtYXJnaW4gYXJvdW5kIHRoZSBwbG90CmxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJpdGFsaWMiKSwgIyBTZXR0aW5nIHRoZSBmb250IGZvciB0aGUgbGVnZW5kIHRleHQKbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92aW5nIHRoZSBsZWdlbmQgdGl0bGUKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjksIDAuOSkpKQpgYGAKCm1pZ2h0IGJlIGNsYW5lciB0byBkbyB0aGUgc2FtZSBwbG90IG9uIG1lYW4gSCBwZXIgeWVhcgoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0pK2dlb21fcG9pbnQoYWVzKHg9eWVhcklELCB5PUgpKSArIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgIAogICAgeWxhYignbWVhbiBob21lIHJ1bnMgcGVyIHllYXInKSsgICAgICAgICAgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgICAgIAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gInBsYWluIiksICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksICAgICAgICAgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKYWRkIGEgbGluZWFyIHRyZW5kbGluZSB1c2luZyBnZW9tX3Ntb290aCBoYXZlIHRvIHNwZWNmaWN5IG1ldGhvZCBmb3IKdGhpcyAobWV0aG9kPSJsbSIgb3IgbWV0aG9kPWxtIGlzIGZpbmUpLiBzZSBpcyBhZGRlZCBieSBkZWZhdWx0IChjYW4gYWRkCnNlPUYgdG8gZGlzYWJsZSB0aGlzKQoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PXllYXJJRF0sIGFlcyh4PXllYXJJRCwgeT1IKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBnZW9tX3Ntb290aChtZXRob2Q9bG0pKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgeGxhYignXG55ZWFyJykrCiAgICB5bGFiKCdtZWFuIGhvbWUgcnVucyBwZXIgeWVhcicpKwogICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAicGxhaW4iKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSxwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgdW5pdHMgPSAsICJjbSIpLCAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSxsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKeW91IGNhbiBhbHNvIGFkZCBhIHNwZWNpZmljIGZvcm11bGEgaW4gZ2VvbV9zbW9vdGggKGUuZy4sIHlcfngreF4yK3heMykKCmBgYHtyfQoocGxvdDEgPSBnZ3Bsb3QoZGF0WywgLihIPW1lYW4oSCkpLCBieT15ZWFySURdLCBhZXMoeD15ZWFySUQsIHk9SCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZ2VvbV9zbW9vdGgoZm9ybXVsYT15fngreF4yK3heMykrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB4bGFiKCdcbnllYXInKSsKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykrICAgICAgICAgICAgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCB1bml0cyA9ICwgImNtIiksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiaXRhbGljIiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSkpCmBgYAoKZmFjZXQgd3JhcCB0aGlzIGNhbiBiZSB1c2VkIHRvIGVhc2lseSBwbG90IGRhdGEgaW4gcGFuZWxzIChlLmcuLCBwbG90Cm1lYW4gaG9tZSBydW5zIG92ZXIgdGltZSBmb3IgZWFjaCBsZWFndWVJRCAtIGhlcmUgSSBhbHNvIGRpc3Rpbmd1aXNoCmxlYWd1ZXMgYnkgY29sb3VyKS8gc2VlaW5nIHNjYWxlcyA9ICJmcmVlX3kiIGJlbG93IG1lYW5zIHRoZSB5IGF4aXMgY2FuCnZhcnkgZnJvbSBwbG90IHRvIHBsb3QuIFlvdSBjYW4gYWxzbyB1c2UgYG5yb3cgPWAgb3IgYG5jb2wgPWAgdG8gc3BlY2lmeQp0aGUgbnVtYmVycyBvZiByb3dzL2NvbHVtbnMKCmBgYHtyfQpkYXQkeWVhcklEZmFjdCA9IGFzLmZhY3RvcihkYXQkeWVhcklEKQoKKHBsb3QxID0gZ2dwbG90KGRhdFssIC4oSD1tZWFuKEgpKSwgYnk9LihsZ0lELCB5ZWFySUQpXSwgYWVzKHg9eWVhcklELCB5PUgsIGNvbG91cj1sZ0lEKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBmYWNldF93cmFwKHZhcnMobGdJRCksIHNjYWxlcyA9ICJmcmVlX3kiKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgICNcbiBhZGRzIGJsYW5rIGxpbmUKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykpCmBgYAoKZmFjZXRfZ3JpZCBkb2VzIGEgc2ltaWxhciB0aGluZyBidXQgb3JnYW5pc2VkIGludG8gY29sdW1ucyBvZiByb3dzCgpoZXJlIHVzZSByb3dzIGJhc2VkIG9uIHRlYW1JRAoKYGBge3J9CihwbG90MSA9IGdncGxvdChkYXRbLCAuKEg9bWVhbihIKSksIGJ5PS4obGdJRCwgeWVhcklEKV0sIGFlcyh4PXllYXJJRCwgeT1ILCBjb2xvdXI9bGdJRCkpKwogICAgZ2VvbV9wb2ludCgpKwogICAgZmFjZXRfZ3JpZChsZ0lEIH4gLikrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB4bGFiKCdcbnllYXInKSsgICAgICAgICAgICAjXG4gYWRkcyBibGFuayBsaW5lCiAgICB5bGFiKCdtZWFuIGhvbWUgcnVucyBwZXIgeWVhcicpKQpgYGAKCmNvbHVtbnMgYmFzZWQgb24gdGVhbUlECgpgYGB7cn0KKHBsb3QxID0gZ2dwbG90KGRhdFssIC4oSD1tZWFuKEgpKSwgYnk9LihsZ0lELCB5ZWFySUQpXSwgYWVzKHg9eWVhcklELCB5PUgsIGNvbG91cj1sZ0lEKSkrCiAgICBnZW9tX3BvaW50KCkrCiAgICBmYWNldF9ncmlkKC4gfiBsZ0lEKSsKICAgIHRoZW1lX2NsYXNzaWMoKSsKICAgIHhsYWIoJ1xueWVhcicpKyAgICAgICAgICAgICNcbiBhZGRzIGJsYW5rIGxpbmUKICAgIHlsYWIoJ21lYW4gaG9tZSBydW5zIHBlciB5ZWFyJykpCmBgYAoKIyMjICoqYmFyIHBsb3RzKioKCiMjIyAqKmJhciBwbG90cyB3aXRoIGVycm9yIGJhcnMgYW5kIGluZGl2aWR1YWwgZGF0YSBwb2ludHMqKgoKQSBzcGVjaWFsIHN1YmNhdGVnb3J5IGFzIHRoaXMgaXMgdGhlIG1vc3QgY29tbW9uIHBsb3QgSSBlbmQgdXAgaGF2aW5nIHRvCmRvLgoKKipOb3RlIG9uIGRhdGEgd3JhbmdsaW5nKioKCiMjIyAqKmJveCBwbG90cyoqCgojIyMgKipleGVyY2lzZXMqKgoKIyMjICoqUmVzb3VyY2VzL0xpbmtzKioKCk5vbiBleGhhdXN0aXZlIGxpc3Qgb2YgbGlua3MvcmVzb3VyY2VzIEkndmUgdXNlZCBpbiB0aGUgY291cnNlIG9mCmNvbXBpbGluZyB0aGlzIG5vdGVib29rCgo8aHR0cHM6Ly9ycHVicy5jb20vamVucmljaG1vbmQvVzZMTD4KCjxodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9nZ3Bsb3QyLmh0bWw+Cgo8aHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQxLVdpdGgtUi1Db2RlLmh0bWw+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YXZpcy8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvZGF0YS12aXMtMi8+Cgo8aHR0cHM6Ly9vdXJjb2RpbmdjbHViLmdpdGh1Yi5pby90dXRvcmlhbHMvcXVhbGl0YXRpdmUvPgoKIyMjICoqZ2dwbG90IGNoZWF0c2hlZXQqKgoKIVtnZ3Bsb3QgY2hlYXRzaGVldF0oaW1hZ2VzL2dncGxvdDItY2hlYXRzaGVldGEucG5nKSAhW2dncGxvdApjaGVhdHNoZWV0XShpbWFnZXMvZ2dwbG90Mi1jaGVhdHNoZWV0Yi5wbmcpCg==